#include <IO/VideoTextMode.h>
#include <IntExcept/IntExcept.h>
#include <IntExcept/Debug.h>
#include <Memory/Paging.h>
#include <Memory/Heap.h>
#include <IntExcept/InteruptHardware.h>
#include <Kernel/GlobalData.h>
#include <Kernel/syscall.h>
#include <Utility/intrinsic.h>
#include <Utility/CLib.h>
#include <FileSystem/Disk.h>
#include <IntExcept/Error.h>

static void ReloadGlobalDescriptorTable(void);
static inline void InitializeInteruptHardware(void);
static void InitializeInteruptDescriptorTable(void);
static void InitializePrimitiveMemoryPool(void);
static void InitializeHeapForKernel(void);
void InitializeKeyboard(void);
static void InitializeFileSystem(void);
void InitializeProcessThread(void);

static GlobalDescriptor s_GDT[64];
static IntGateDescriptor s_IDT[256];

void SimpleMain(void)
{

    kmessage("[INFO] Welcome to SimpleOS! Start initializing the KERNEL.\r\n");
    InitializeInteruptHardware();
    ReloadGlobalDescriptorTable();
    InitializeInteruptDescriptorTable(); /* 初始化 IDT */
    QueryMemoryInfo(); /* 获取可用内存信息 */
    InitializePrimitiveMemoryPool(); /* 创建原始内存池 */
    InitializeHeapForKernel(); /* 在内存池的基础上构建内核堆 */
    InitializeKeyboard();
    InitializeFileSystem();
    InitializeProcessThread();
}

/*
@brief 重新加载 GDT
*/
static void ReloadGlobalDescriptorTable(void)
{
    // GlobalDescriptor* lpGDT = HeapAllocate(g_lpKernelHeap, 0, 256); /* 可容纳 32 个描述符 */
    /* 遵照引导加载器中的 GDT 布局方式，前两个分别为代码段和数据段，这样可以不变动 CS 和 DS 的值 */
    s_GDT[1] = MAKE_SEGMENT_DESCRIPTOR(0xFFFFF, 0, 0b1000, 1, 0, 1, 0, 0, 1, 1); /* Kernel Code Segment, execute only */
    s_GDT[2] = MAKE_SEGMENT_DESCRIPTOR(0xFFFFF, 0, 0b0010, 1, 0, 1, 0, 0, 1, 1); /* Kernel Data Segment, read/write */
    s_GDT[3] = MAKE_SEGMENT_DESCRIPTOR(0xBFFFF, 0, 0b1000, 1, 3, 1, 0, 0, 1, 1); /* User Code Segment, execute only */
    s_GDT[4] = MAKE_SEGMENT_DESCRIPTOR(0xBFFFF, 0, 0b0010, 1, 3, 1, 0, 0, 1, 1); /* User Data Segment, read/write */
    QWORD qwGDTR = ((sizeof(s_GDT) - 1) | ((QWORD)s_GDT << 16));
    LOAD_GDT_32(&qwGDTR);
}

/* 
@brief 初始化中断硬件
*/
static inline void InitializeInteruptHardware(void)
{
/* 8259A 可编程中断芯片初始化 */
#ifdef _8259A_
    /* 初始化主片 */
    OUT_BYTE(PIC_MASTER_CTRL_PORT, 0x11);
    OUT_BYTE(PIC_MASTER_DATA_PORT, 0x20); /* 0x20 ~ 0x27 */
    OUT_BYTE(PIC_MASTER_DATA_PORT, 0x04); /* IRQ2 接 slave */
    OUT_BYTE(PIC_MASTER_DATA_PORT, 0x01); /* EOI */
    /* 初始化从片 */
    OUT_BYTE(PIC_SLAVE_CTRL_PORT, 0x11);
    OUT_BYTE(PIC_SLAVE_DATA_PORT, 0x28);
    OUT_BYTE(PIC_SLAVE_DATA_PORT, 0x02); /* 连接到主片的 IRQ2 */
    OUT_BYTE(PIC_SLAVE_DATA_PORT, 0x01); /* EOI */

    OUT_BYTE(PIC_MASTER_DATA_PORT, 0xFE);
    OUT_BYTE(PIC_SLAVE_DATA_PORT, 0xFF);
#endif
/* 8253 计数器初始化 */
#ifdef _8253_
    OUT_BYTE(PIT_CONTROL_WORD_PORT, PIT_COUNTER0_SELECTOR | PIT_COUNTER0_RW_MODE | PIT_COUNTER0_MODE);
    OUT_BYTE(PIT_COUNTER0_PORT, (BYTE)PIT_COUNTER0_INIT_VALUE); /* 低 8 位 */
    OUT_BYTE(PIT_COUNTER0_PORT, (BYTE)(PIT_COUNTER0_INIT_VALUE) >> 8); /* 高 8 位 */
#endif
    ksuccess("[OK] Successfully initialize interupt hardware.\r\n");
}

/*
@brief 初始化 IDT
@note IDT 由所有 CPU 核心共享
*/
static void InitializeInteruptDescriptorTable(void)
{
    memset(s_IDT, 0, sizeof(s_IDT));
    for (int i = 0; i < sizeof(g_aIntExceptEntrypoint) / sizeof(void(*)(void)); i++)
    {
        s_IDT[i] = MAKE_INT_GATE_DESCRIPTOR((DWORD_PTR)g_aIntExceptEntrypoint[i], SELECTOR_KERNEL_CODE, INT_GATE_USER, INT_GATE_PRESENT); /* 中断门特权级必须为用户 */
    }
    s_IDT[0x80] = MAKE_INT_GATE_DESCRIPTOR((DWORD_PTR)DispatchSyscall, SELECTOR_KERNEL_CODE, INT_GATE_USER, INT_GATE_PRESENT);
    QWORD qwIDTR = (QWORD)(sizeof(s_IDT) - 1) | ((QWORD)(s_IDT) << 16); /* 注意小端方式，IDTR 本身占 48 B，因此只能用 QWORD 存放 */;
    LOAD_IDT_32(&qwIDTR);
}


/*
@brief 开辟原始内存池
@return 内存池中包含的页面数量
@note 开辟大小为 2 MiB 的原始内存池（必须为连续的物理内存块），返回值即原始内存池实际包含的页面数（512 个页面）
*/
void InitializePrimitiveMemoryPool(void)
{
    LPAddressRangeDescriptor lpARDS = g_MemoryAddressMap;
    while (lpARDS->dwType != 0) 
    {
        if (lpARDS->dwType == 1 && lpARDS->dwlpBaseAddrLow >= 0x100000 && lpARDS->dwLengthLow >= 16 * 1024 * 1024) /* 找到实模式 1MiB 以外的第一块可用的 2 MiB 连续物理内存 */
        {
            g_dwlpPrimitiveMemoryPool = (lpARDS->dwlpBaseAddrLow); /* 内存池基址 */
            break;
        }     
        lpARDS++;
    }
    /* 内存池必须正确初始化，且内存池基址非 NULL */
    KERNEL_ASSERT(g_dwlpPrimitiveMemoryPool != 0, "Fails to initialize memory pool.\r\n");
    /* 物理内存池位图初始化，全部置为 1 表示可用 */
    memset(g_bmPrimitiveMemoryPool, 0xFF, sizeof(g_bmPrimitiveMemoryPool));
    /* BootLoader 中曾将页目录 0 号 PDE 指向 768 号页表，进入内核后需要清除 */
    g_MasterPageTable[1022][0] = 0;
    /* 配置页表信息结构体，记录每个页表此时的状态 */
    /* 768 号页表中 0 ~ 255 PTE 倍占用，用于映射实模式 1 MiB 内存空间 */
    ksuccess("[OK] Successfully initializes primitive memory pool.\r\n");
}

/*
@brief 初始化内核堆
*/
static void InitializeHeapForKernel(void)
{
    KERNEL_ASSERT(
        (g_lpKernelHeap = AllocatePageForKernel(1, PTE_WRITEABLE)), \
        "Fails to initialize kernel heap." \
    );
    memset(g_lpKernelHeap, 0, PAGE_SIZE); /* 第一页作为 Arena Descriptor Table，清空 */
    /* 16 B Unit Arena */
    g_lpKernelHeap[0].wGranularity = 16;
    InitializeArenaForKernel(&g_lpKernelHeap[0]);
    /* 32 B Unit Arena */
    g_lpKernelHeap[1].wGranularity = 32;
    InitializeArenaForKernel(&g_lpKernelHeap[1]);
    /* 64 B Unit Arena */
    g_lpKernelHeap[2].wGranularity = 64;
    InitializeArenaForKernel(&g_lpKernelHeap[2]);
    /* 128 B Unit Arena */
    g_lpKernelHeap[3].wGranularity = 128;
    InitializeArenaForKernel(&g_lpKernelHeap[3]);
    /* 256 B Unit Arena */
    g_lpKernelHeap[4].wGranularity = 256;
    InitializeArenaForKernel(&g_lpKernelHeap[4]);
    /* 512 B Unit Arena */
    g_lpKernelHeap[5].wGranularity = 512;
    InitializeArenaForKernel(&g_lpKernelHeap[5]);
    /* 1024 B Unit Arena */
    g_lpKernelHeap[6].wGranularity = 1024;
    InitializeArenaForKernel(&g_lpKernelHeap[6]);
    /* 2048 B Unit Arena */
    g_lpKernelHeap[7].wGranularity = 2048;
    InitializeArenaForKernel(&g_lpKernelHeap[7]);
    g_lpIntStack = HeapAllocate(g_lpKernelHeap, HEAP_ZERO_MEMORY, 4096);
    g_lpIntStack[GetCurrentCPUCore()] = HeapAllocate(g_lpKernelHeap, 0, 4096);
}

void InitializeKeyboard(void)
{
    g_lpKeyboardInputBuffer = CreateKeyboardInputBuffer(4096);
    OUT_BYTE(PIC_MASTER_DATA_PORT, 0xFC);
}

void InitializeFileSystem()
{
    ReadDisk(&g_BPB, 0, 1);
    g_FCBCache.lpszPath = "\\"; /* 根目录 */
    g_FCBCache.dwFileType = FILE_DIRECTORY;
    g_FCBCache.dwFirstCluster = g_BPB.BPB_RootClus; /* 根目录首簇号 */
    g_FCBCache.dwFirstSector = 8 * (g_FCBCache.dwFirstCluster - 2) /* 去掉 BPB 和 FAT 这两个簇 */
                + g_BPB.BPB_ResvdSecCnt /* BPB 占用的扇区数 */
                + (g_BPB.BPB_NumFATs * g_BPB.BPB_FATSz32); /* FAT 占用的扇区数 */
    g_FCBCache.lpNext = NULL;
}


/*
@brief 初始化进程管理
*/
static void InitializeProcessThread(void)
{
    /* 初始化线程 TCB 就绪队列 */
    LPTCBQUEUE lpQueue;
    lpQueue = (LPVOID)(0xFFFFE000 & (DWORD)&lpQueue); /* 在内核堆中分配两个页面用作线程的内核栈，线程的内核栈底存放 TCB */
    lpQueue->context.dwCS = SELECTOR_KERNEL_CODE;
    lpQueue->context.dwDS = SELECTOR_KERNEL_DATA;
    lpQueue->context.dwES = 0;
    lpQueue->context.dwFS = 0;
    lpQueue->context.dwGS = 0;
    lpQueue->context.dwSS = SELECTOR_KERNEL_DATA;
    lpQueue->context.dwEAX = 0;
    lpQueue->context.dwECX = 0;
    lpQueue->context.dwEDX = 0;
    lpQueue->context.dwEBX = 0;
    lpQueue->context.dwESI = 0;
    lpQueue->context.dwEDI = 0;
    lpQueue->lpKernelStack = (LPVOID)((DWORD)lpQueue + PAGE_SIZE * 2); /* 初始栈顶 */
    lpQueue->context.dwESP = (DWORD)lpQueue + PAGE_SIZE * 2; /* 初始栈顶 */
    lpQueue->context.dwEBP = (DWORD)lpQueue + PAGE_SIZE * 2; /* 初始栈顶 */
    lpQueue->context.dwEFLAGS = READ_EFLAGS_32();
    // lpQueue->context.dwEIP = (DWORD)InitializeMainThread;
    lpQueue->bIsIdle = TRUE; /* 创建一个 Idle 线程，也即双向链表的头结点 */
    lpQueue->dwPriority = 1;
    lpQueue->nTicks = 1;
    lpQueue->lpPrev = lpQueue;
    lpQueue->lpNext = lpQueue;
    // lpQueue->lpStartAddress = InitializeMainThread;
    lpQueue->lpParameter = NULL;
    lpQueue->lpPrevSibling = NULL;
    lpQueue->lpNextSibling = NULL;
    lpQueue->dwPriority = 3;
    lpQueue->dwUserStackSize = 8192;
    g_lpReadyTCBQueue = lpQueue;
    /* 初始化 TSS */
    LPTaskStateSegment lpTSS = HeapAllocate(g_lpKernelHeap, 0, sizeof(TaskStateSegment));
    /* 在堆中分配的内存终究要通过栈上的变量或者静态声明周期的变量访问 */
    lpTSS->dwRing0StaskSegment = SELECTOR_KERNEL_DATA; /* 特权级切换时，转入 Ring-0 栈段 */
    lpTSS->dwRing0ESP = (DWORD)lpQueue->lpKernelStack; /* 特权级切换时，使用此栈 */
    lpTSS->wIOMapBaseAddress = sizeof(TaskStateSegment);
    s_GDT[5] = MAKE_SEGMENT_DESCRIPTOR(sizeof(TaskStateSegment) - 1 /* LIMIT */, lpTSS /* BASE */, 0b1001 /* TYPE */, 0 /* S */, 0b00 /* DPL */, 1 /* P */, 0 /* AVL */, 0 /* L */, 0 /* DB */, 0 /* G */); /* 任务状态段 */
    LOAD_TR_32(MAKE_SEGMENT_SELECTOR(0, 0, 5));
    /* 初始化进程 */
    g_ProcessTree.dwCR3 = READ_CR3_32();
    g_ProcessTree.dwProcessId = 0;
    g_ProcessTree.lpszName = "System";
    g_ProcessTree.lpszExecPath = "\\PROGRAM\\SYSTEM.EXE";
    g_ProcessTree.lpThreadList = g_lpReadyTCBQueue;
    g_lpReadyTCBQueue->lpProcess = &g_ProcessTree;
    InitializeMainThread(); /* 在主线程中完成剩余初始化工作后运行 System 进程的主线程 */
}
